package cn.coufran.springboot.starter.http;

import cn.coufran.springboot.starter.http.content.HttpRequestBodyContent;
import cn.coufran.springboot.starter.http.exception.HttpException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.IOException;
import java.io.InputStream;
import java.net.URI;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.util.Map;
import java.util.UUID;

/**
 * HTTP客户端
 * @author Coufran
 * @version 2.0.0
 * @since 1.0.0
 */
public class HttpClient {
    /** 日志Logger */
    private static final Logger LOGGER = LoggerFactory.getLogger(HttpClient.class);

    /** 默认的客户端实例 */
    public static HttpClient INSTANCE_DEFAULT = new HttpClient();

    /**
     * 执行HTTP请求
     * @param request HTTP请求
     * @return HTTP响应
     * @throws HttpException HTTP异常
     */
    public HttpResponse execute(HttpRequest request) {
        LOGGER.info("request:{}", request);

        // Request适配
        // 构造URL
        StringBuilder url = new StringBuilder(request.url()).append("?");
        request.params().forEach((key, value) -> {
            key = URLEncoder.encode(key, StandardCharsets.UTF_8);
            value = URLEncoder.encode(value, StandardCharsets.UTF_8);
            url.append(key).append("=").append(value).append("&");
        });
        url.deleteCharAt(url.length() - 1);

        // 构造HttpRequest
        java.net.http.HttpRequest.Builder javaRequestBuilder = java.net.http.HttpRequest.newBuilder();
        javaRequestBuilder.uri(URI.create(url.toString()));
        switch (request.method()) {
            case GET:
                javaRequestBuilder.GET();
                javaRequestBuilder.header(HttpHeaders.CONTENT_TYPE, request.contentType().toString());
                break;
            case POST:
                java.net.http.HttpRequest.BodyPublisher bodyPublisher;
                if (request.contents() == null || request.contents().isEmpty()) {
                    bodyPublisher = java.net.http.HttpRequest.BodyPublishers.noBody();
                    javaRequestBuilder.header(HttpHeaders.CONTENT_TYPE, request.contentType().toString());
                } else {
                    if (request.contentType().getMediaType().isMultiPart()) {
                        MultipartBody.Builder mutipartBuilder = MultipartBody.builder();
                        for (HttpRequestBodyContent content : request.contents()) {
                            content.accept(mutipartBuilder);
                        }
                        String boundary = UUID.randomUUID().toString().replaceAll("-", "");
                        bodyPublisher = BodyPublishers.ofMultipart(mutipartBuilder.build(), boundary);
                        javaRequestBuilder.header(HttpHeaders.CONTENT_TYPE, request.contentType().toString(boundary));
                    } else {
                        InputStream in = request.contents().getFirst().getInputStream();
                        bodyPublisher = java.net.http.HttpRequest.BodyPublishers.ofInputStream(() -> in);
                        javaRequestBuilder.header(HttpHeaders.CONTENT_TYPE, request.contentType().toString());
                    }
                }
                javaRequestBuilder.POST(bodyPublisher);
                break;
            default:
                throw new IllegalStateException("unsupported method " + request.method());
        }

        // 设置请求头
        Map<String, String> headers = request.headers();
        headers.forEach(javaRequestBuilder::header);

        // 请求
        java.net.http.HttpClient javaClient = java.net.http.HttpClient.newHttpClient();
        java.net.http.HttpResponse<byte[]> javaResponse;
        try {
            javaResponse = javaClient.send(
                    javaRequestBuilder.build(), java.net.http.HttpResponse.BodyHandlers.ofByteArray()
            );
        } catch (IOException | InterruptedException e) {
            throw new HttpException(e);
        }

        // Response适配
        int statusCode = javaResponse.statusCode();
//        HttpHeaders responseHeaders = response.headers();
//        responseHeaders.map().forEach((name, values) -> {
//            String value = values.getFirst();
//        });
        String contentTypeValue = javaResponse.headers().firstValue(HttpHeaders.CONTENT_TYPE).orElse(null);
        ContentType contentType;
        if (contentTypeValue == null) {
            contentType = ContentType.of(MediaType.APPLICATION__JSON);
        } else {
            contentType = ContentType.of(contentTypeValue);
        }
        byte[] body = javaResponse.body();
        HttpResponse response = new HttpResponse(statusCode, contentType, body);

        javaClient.close();

        LOGGER.info("response:{}", response);
        return response;
    }


}
